package hu.vissy.yacap.option;

import hu.vissy.yacap.exception.CommandLineParserException;

/**
 * Builder utility class for {@linkplain CommandLineOption}.
 *
 * @author balage
 *
 */
public abstract class CommandLineOptionBuilder<T, B extends CommandLineOptionBuilder<T, B>> {
    protected Class<?> dataType;
    protected char shortForm = '\0';
    protected String longForm;
    protected String description = "";
    protected String valueName;
    protected T defaultValue;
    protected String valueKey = null;
    protected OptionPolicy defaultPolicy = OptionPolicy.OPTIONAL;
    protected boolean valueOptional;

    /**
     * Constructor.
     *
     * @param type
     *            The type of the option.
     * @param longForm
     *            The long form of the option.
     */
    public CommandLineOptionBuilder(Class<?> dataType, String longForm) {
        this.dataType = dataType;
        this.longForm = longForm;
        valueName = dataType.getSimpleName().toLowerCase();
    }

    /**
     * Optional short (one-character) option code.
     *
     * @param shortForm
     *            The short option form.
     * @return The builder object itself.
     */
    public B withShortForm(char shortForm) {
        this.shortForm = shortForm;
        return getThis();
    }

    /**
     * Az opció részletes leírását állítja be.
     *
     * @param description
     *            Részletes leírás.
     * @return The builder object itself.
     */
    public B withDescription(String description) {
        this.description = description;
        return getThis();
    }


    /**
     * Sets the value key.
     *
     * @param key
     *            The key for result value reference.
     * @return The builder object itself.
     */
    public B withValueKey(String key) {
        this.valueKey = key;
        return getThis();
    }



    /**
     * Paraméterrel rendelkező opció paraméterének megnevezése a
     * dokumentációban.
     *
     * @param valueName
     *            A paraméter neve.
     * @return The builder object itself.
     */
    public B withValueName(String valueName) {
        this.valueName = valueName;
        return getThis();
    }

    /**
     * Sets the value policy.
     *
     * @param valuePolicy
     *            The value policy to use.
     * @return The builder object itself.
     */
    public B withValueOptional() {
        this.valueOptional = true;
        return getThis();
    }

    /**
     * Automatikus beállítás esetén használandó alapértelmezett érték.
     *
     * <p>
     * Ha nem adjuk meg, akkor az argumentum hiánya esetén nem történik
     * értékadás a megadott mezőn, így annak értéke nem változik.
     * </p>
     *
     * @param defaultValue
     *            Az alapértelemezett érték.
     * @return The builder object itself.
     */
    public B withDefaultValue(T defaultValue) {
        this.defaultValue = defaultValue;
        return getThis();
    }

    /**
     * Sets the default behavior of the option.
     *
     * @param policy
     *            The default policy.
     * @return The builder object itself.
     */
    public B withDefaultOptionPolicy(OptionPolicy policy) {
        this.defaultPolicy = policy;
        return getThis();
    }

    protected abstract B getThis();

    protected void checkData() {
        if (valueOptional && defaultValue == null) {
            throw new CommandLineParserException("Default value should be set with optional value policy.");
        }
    }

    protected abstract CommandLineOption<T> createInstance();

    public CommandLineOption<T> getInstance() {
        checkData();
        CommandLineOption<T> res = createInstance();
        fillInInstance(res);
        return res;
    }

    protected void fillInInstance(CommandLineOption<T> option) {

        if (option.getValuePolicy() != ValuePolicy.NONE) {
            option.setDefaultValue(defaultValue);
        }
        option.setShortForm(shortForm);
        option.setDescription(description);
        option.setDefaultOptionPolicy(defaultPolicy);
        option.setValueName(valueName);
        option.setValueKey(valueKey);
        if (valueOptional) {
            option.setValuePolicy(ValuePolicy.OPTIONAL);
        }
    }

}